﻿using System.ComponentModel;
using System;
using DeepEarth.Map.Core;
using System.Collections.ObjectModel;
using System.Windows.Media;
using System.Windows;
using DeepEarth.Core;
using System.Collections;
using System.Collections.Generic;
using DeepEarth.Map.Core;
namespace DeepEarth.Toolkit.Geometry.Data
{
    public abstract class Geometry : INotifyPropertyChanged, IRemovable, ISelectable
    {
        public Geometry()
        {
            Id = Guid.NewGuid().ToString();
        }

        #region Properties

        PositionOrigin positionOrigin;
        public PositionOrigin PositionOrigin
        {
            get
            {
                return positionOrigin;
            }
            set
            {
                positionOrigin = value;
                OnPropertyChanged("PositionOrigin");
            }
        }


        string id;
        public string Id
        {
            get { return id; }
            set
            {
                if (value != id)
                {
                    id = value;
                    OnPropertyChanged("Id");
                }
            }
        }

        double fullOpacity = 1;
        /// <summary>
        /// The opacity of the geometry when it is fully visible
        /// </summary>
        public double FullOpacity
        {
            get { return fullOpacity; }
            set
            {
                if (value != fullOpacity)
                {
                    // FullOpacity is only set in edit mode so current opacity should immediately reflect this.
                    currentOpacity = fullOpacity = value;
                    OnPropertyChanged("FullOpacity", "CurrentOpacity");
                }
            }
        }

        double currentOpacity = 1;
        public double CurrentOpacity
        {
            get { return currentOpacity; }
            set
            {
                if (value != currentOpacity)
                {
                    currentOpacity = value;
                    OnPropertyChanged("CurrentOpacity");
                }
            }
        }

        bool isEditable = true;

        public bool IsEditable
        {
            get
            {
                return isEditable;
            }
            set
            {
                isEditable = value;
                OnPropertyChanged("IsEditable");
            }
        }


        bool isVisible = true;

        public bool IsVisible
        {
            get
            {
                return isVisible;
            }
            set
            {
                isVisible = value;
                OnPropertyChanged("IsVisible");
            }
        }

        #endregion

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(params string[] propertyNames)
        {
            var handler = PropertyChanged;
            if (handler != null)
                foreach (var propertyName in propertyNames)
                    handler(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

        public System.Windows.Input.ICommand RemoveCommand {get;set;}
        public System.Windows.Input.ICommand SelectionCommand { get; set; }
    }

    public class PointGeometry : Geometry, ILocatable
    {
        #region Properties
        Location position;
        public Location Position
        {
            get { return position; }
            set
            {
                if (value != position)
                {
                    position = value;
                    OnPropertyChanged("Position");
                }
            }
        }
        #endregion
    }

    public class ShapeGeometry : Geometry, IMultiLocation
    {
        #region Properties
        // Should this be a collection of points?
        protected ObservableCollection<Location> locations = new ObservableCollection<Location>();
        public ObservableCollection<Location> Locations
        {
            get { return locations; }
            set
            {
                if (value != locations)
                {
                    if (locations != null)
                    {
                        locations.CollectionChanged -= locations_CollectionChanged;
                    }

                    locations = value;

                    locations.CollectionChanged += locations_CollectionChanged;
                    OnPropertyChanged("Locations");
                }
            }
        }

        IEnumerable<Location> IMultiLocation.Locations
        {
            get
            {
                return Locations;
            }
        }

        protected LocationRectangle boundingBox;
        public LocationRectangle BoundingBox
        {
            get
            {
                if (boundingBox == null)
                {
                    boundingBox = new LocationRectangle(this.Locations);
                }
                return boundingBox;
            }
        }


        void locations_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            boundingBox = new LocationRectangle(this.Locations);
        }

        protected Location position;
        public Location Position
        {
            get
            {
                if (position != null)
                {
                    return position;
                }
                else
                {
                    return BoundingBox.Center;
                }
            }
            set
            {
                position = value;
            }
        }

        protected Brush stroke;
        public Brush Stroke
        {
            get
            {
                return stroke;
            }
            set
            {
                stroke = value;
                OnPropertyChanged("Stroke");
            }
        }

        protected double strokeThickness;
        public double StrokeThickness
        {
            get
            {
                return strokeThickness;
            }
            set
            {
                strokeThickness = value;
                OnPropertyChanged("StrokeThickness");
            }
        }

        protected ObservableCollection<double> strokeDashArray;
        public ObservableCollection<double> StrokeDashArray
        {
            get
            {
                return strokeDashArray;
            }
            set
            {
                strokeDashArray = value;
                OnPropertyChanged("StrokeDashArray");
            }
        }


        Brush fill;
        public Brush Fill
        {
            get
            {
                return fill;
            }
            set
            {
                fill = value;
                OnPropertyChanged("Fill");
            }
        }

        #endregion

        public ShapeGeometry FillClone(ShapeGeometry clone)
        {
            clone.position = this.position;
            clone.stroke = this.stroke;
            clone.strokeThickness = this.strokeThickness;
            clone.fill = this.fill;

            foreach (Location l in this.locations)
            {
                clone.Locations.Add(l.Clone());
            }

            return clone;
        }

        public virtual ShapeGeometry Clone()
        {
            ShapeGeometry clone = new ShapeGeometry();
            return FillClone(clone);
        }
    }

    public class PushpinGeometry : PointGeometry
    {
        Color fill;
        public Color Fill
        {
            get
            {
                return fill;
            }
            set
            {
                fill = value;
                OnPropertyChanged("Fill");
            }
        }

        double scale;
        public double Scale
        {
            get
            {
                return scale;
            }
            set
            {
                scale = value;
                OnPropertyChanged("Scale");
            }
        }
    }

    public class LineStringGeometry : ShapeGeometry
    {
        public override ShapeGeometry Clone()
        {
            LineStringGeometry clone = new LineStringGeometry();
            return FillClone(clone);
        }
    }

    public class PolygonGeometry : ShapeGeometry
    {
        public override ShapeGeometry Clone()
        {
            PolygonGeometry clone = new PolygonGeometry();
            base.FillClone(clone);

            clone.innerStroke = this.innerStroke;
            clone.innerStrokeThickness = this.innerStrokeThickness;

            if (innerRings != null)
            {
                foreach (var ring in innerRings)
                {
                    var innerRing = new ObservableCollection<Location>();

                    foreach (Location l in ring)
                    {
                        innerRing.Add(l.Clone());
                    }

                    clone.InnerRings.Add(innerRing);
                }
            }

            return clone;
        }

        public ObservableCollection<Location> OuterRing
        {
            get { return locations; }
            set
            {
                if (value != locations)
                {
                    locations = value;
                    OnPropertyChanged("OuterRing");
                }
            }
        }

        private ObservableCollection<ObservableCollection<Location>> innerRings;

        public ObservableCollection<ObservableCollection<Location>> InnerRings
        {
            get
            {
                if (innerRings == null)
                {
                    innerRings = new ObservableCollection<ObservableCollection<Location>>();
                }
                return innerRings;
            }
            set
            {
                innerRings = value;
                OnPropertyChanged("InnerRings");
            }
        }

        Brush innerStroke;
        public Brush InnerStroke
        {
            get
            {
                if (innerStroke == null)
                {
                    return stroke;
                }
                else
                {
                    return innerStroke;
                }
            }
            set
            {
                innerStroke = value;
                OnPropertyChanged("InnerStroke");
            }
        }

        double innerStrokeThickness;
        public double InnerStrokeThickness
        {
            get
            {
                if (innerStrokeThickness == null)
                {
                    return strokeThickness;
                }
                else
                {
                    return innerStrokeThickness;
                }
            }
            set
            {
                innerStrokeThickness = value;
                OnPropertyChanged("InnerStrokeThickness");
            }
        }


    }

    public class LabelGeometry : PointGeometry
    {
        string text;
        public string Text
        {
            get
            {
                return text;
            }
            set
            {
                text = value;
                OnPropertyChanged("Text");
            }

        }

        double fontsize = 18;
        public double FontSize
        {
            get
            {
                return fontsize;
            }
            set
            {
                fontsize = value;
                OnPropertyChanged("FontSize");
            }
        }

        Color textColor;
        public Color TextColor
        {
            get
            {
                return textColor;
            }
            set
            {
                textColor = value;
                OnPropertyChanged("TextColor");
            }
        }

    }

    [AtomicEntity(true)]
    public class GeometryCollection : Geometry, IItemsContainer
    {
        private ObservableCollection<Geometry> geometries = new ObservableCollection<Geometry>();
        public ObservableCollection<Geometry> Geometries
        {
            get
            {
                return geometries;
            }
            set
            {
                Geometries = value;
            }
        }

        public IEnumerable Children
        {
            get
            {
                return Geometries;
            }
        }

        public bool IsAtomic
        {
            get { return true; }
        }

        double opacity = 1.0;
        public double Opacity
        {
            get { return opacity; }
            set
            {
                if (opacity != value)
                {
                    opacity = value;
                    OnPropertyChanged("Opacity");
                }
            }
        }

        ActivationCascadeMode acm = ActivationCascadeMode.Activate | ActivationCascadeMode.Deactivate;
        public ActivationCascadeMode ActivationCascade
        {
            get { return acm; }
        }

    }
}
